package com.emagic.manage.data.oss;

import android.content.Context;
import android.support.annotation.NonNull;
import android.util.Log;

import com.alibaba.sdk.android.oss.ClientConfiguration;
import com.alibaba.sdk.android.oss.OSS;
import com.alibaba.sdk.android.oss.OSSClient;
import com.alibaba.sdk.android.oss.callback.OSSCompletedCallback;
import com.alibaba.sdk.android.oss.common.auth.OSSCredentialProvider;
import com.alibaba.sdk.android.oss.common.auth.OSSPlainTextAKSKCredentialProvider;
import com.alibaba.sdk.android.oss.model.GetObjectRequest;
import com.alibaba.sdk.android.oss.model.GetObjectResult;
import com.alibaba.sdk.android.oss.model.ObjectMetadata;
import com.alibaba.sdk.android.oss.model.PutObjectRequest;
import com.alibaba.sdk.android.oss.model.PutObjectResult;
import com.emagic.manage.data.BuildConfig;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;

import javax.inject.Inject;
import javax.inject.Singleton;

import rx.Observable;
import rx.Subscriber;
import rx.exceptions.Exceptions;
import rx.functions.Action0;
import rx.schedulers.Schedulers;
import rx.subscriptions.Subscriptions;

/**
 * 阿里云OSS上传帮助类
 *
 * @author baoy
 * @version 1.0
 *          Create by 16/4/22 下午3:10
 */
@Singleton
public class OSSFileHelper {

    private final String endpoint = BuildConfig.OSS_BASE_URL;
    private final String callbackAddress = "";                      //上传的callback地址
    private String bucket = BuildConfig.OSS_BUCKET_NAME;

    private Context context;

    private OSSService ossService;

    @Inject
    public OSSFileHelper(@NonNull Context context) {
        this.context = context;

        this.ossService = initOSS(endpoint, bucket, null);

    }

    /**
     * 初始化OSS Client
     */
    private OSSService initOSS(String endpoint, String bucket, ImageDisplayer displayer) {
        //如果希望直接使用accessKey来访问的时候，可以直接使用OSSPlainTextAKSKCredentialProvider来鉴权。
        final String accessKey = BuildConfig.OSS_ACCESS_KEY;
        final String secretKey = BuildConfig.OSS_SECRET_KEY;
        //使用自己的获取STSToken的类
        OSSCredentialProvider credentialProvider = new OSSPlainTextAKSKCredentialProvider(accessKey, secretKey);

        ClientConfiguration conf = new ClientConfiguration();
        conf.setConnectionTimeout(15 * 1000); // 连接超时，默认15秒
        conf.setSocketTimeout(15 * 1000); // socket超时，默认15秒
        conf.setMaxConcurrentRequest(5); // 最大并发请求书，默认5个
        conf.setMaxErrorRetry(2); // 失败后最大重试次数，默认2次

        OSS oss = new OSSClient(context, endpoint, credentialProvider, conf);

        return new OSSService(oss, bucket, displayer);
    }

    /**
     * 获取OSS文件远程地址
     */
    public static String getFileRemoteUrl(String path) {
        return String.format("http://%s.%s/%s", BuildConfig.OSS_BUCKET_NAME, BuildConfig.OSS_BASE_URL, path);
    }

    /**
     * 异步获取OSS对象
     *
     * @param object   对象名称
     * @param callback 客户端回调
     */
    public void asyncGet(String object, final OSSCompletedCallback<GetObjectRequest, GetObjectResult> callback) {
        ossService.asyncGetImage(object, callback);
    }


    /**
     * rx异步获取OSS对象
     *
     * @param object
     * @param localFilePath
     * @return
     */
    public Observable<File> asyncGet(String object, String localFilePath) {


        return Observable.create(new OnDownloadCallOnSubscribe<>(object, localFilePath)).subscribeOn(Schedulers.newThread());

    }


    /**
     * 异步上传
     *
     * @param type
     * @param localFile
     * @param userCallback
     */
    public void asyncUpload(int type, String localFile, @NonNull final OSSCompletedCallback<PutObjectRequest, PutObjectResult> userCallback) {

        String object = generateFileName();

//        switch (type) {
//            case 0:
//            case 1:
//            case 2:
//            case 5:
//                object = CommonUtils.generateFileName(CommonUtils.FileType.FILE_TYPE_IMAGE);
//                break;
//            case 3:
//                object = CommonUtils.generateFileName(CommonUtils.FileType.FILE_TYPE_AUDIO);
//                break;
//            case 4:
//                object = CommonUtils.generateFileName(CommonUtils.FileType.FILE_TYPE_VIDEO);
//                break;
//        }

        ossService.asyncPutImage(object, localFile, userCallback, null);

    }

    /**
     * rx异步上传
     *
     * @param type
     * @param localFilePath
     * @return
     */
    public Observable<String> asyncUpload(final int type, final String localFilePath) {

        String object = generateFileName();

//        switch (type) {
//            case 0:
//            case 1:
//            case 2:
//            case 5:
//                object = CommonUtils.generateFileName(CommonUtils.FileType.FILE_TYPE_IMAGE);
//                break;
//            case 3:
//                object = CommonUtils.generateFileName(CommonUtils.FileType.FILE_TYPE_AUDIO);
//                break;
//            case 4:
//                object = CommonUtils.generateFileName(CommonUtils.FileType.FILE_TYPE_VIDEO);
//                break;
//        }

        return Observable.create(new OnUploadCallOnSubscribe<String>(object, localFilePath)).subscribeOn(Schedulers.newThread());

    }

    private String generateFileName() {
        String fileName = UUID.randomUUID().toString();
//        switch() {
//            case 1:
//                fileName = fileName.concat(".").concat("png");
//                break;
//            case 2:
//                fileName = fileName.concat(".").concat("arm");
//                break;
//            case 3:
//                fileName = fileName.concat(".").concat("mp4");
//                break;
//            case 4:
//                fileName = fileName.concat(".").concat("tmp");
//                break;
//            case 5:
//                fileName = fileName.concat(".").concat("thumbnail");
//        }

        return fileName;
    }


    final class OnUploadCallOnSubscribe<T> implements Observable.OnSubscribe<String> {

        private String object;
        private String path;

        public OnUploadCallOnSubscribe(String object, String path) {
            this.object = object;
            this.path = path;
        }

        @Override
        public void call(Subscriber<? super String> subscriber) {


            // Attempt to cancel the call if it is still in-flight on unsubscription.
            subscriber.add(Subscriptions.create(new Action0() {
                @Override
                public void call() {

                }
            }));

            try {
                PutObjectResult result = ossService.putImage(object, path);
                if (!subscriber.isUnsubscribed()) {
                    subscriber.onNext(object);
                }

            } catch (Throwable t) {
                Exceptions.throwIfFatal(t);
                if (!subscriber.isUnsubscribed()) {
                    subscriber.onError(t);
                }
                return;
            }

            if (!subscriber.isUnsubscribed()) {
                subscriber.onCompleted();
            }
        }
    }

    final class OnDownloadCallOnSubscribe<T> implements Observable.OnSubscribe<File> {

        private String object;
        private String path;

        public OnDownloadCallOnSubscribe(String object, String path) {
            this.object = object;
            this.path = path;
        }

        @Override
        public void call(Subscriber<? super File> subscriber) {


            // Attempt to cancel the call if it is still in-flight on unsubscription.
            subscriber.add(Subscriptions.create(new Action0() {
                @Override
                public void call() {

                }

            }));
            InputStream inputStream = null;
            FileOutputStream fileOutputStream = null;

            try {
                GetObjectResult result = ossService.getObject(object);
                Log.d("OSSFileHelper", "Content-Length:" + result.getContentLength());

                File outFile = new File(path);
                // 获取文件输入流
                inputStream = result.getObjectContent();
                fileOutputStream = new FileOutputStream(outFile);
                byte[] buffer = new byte[2048];
                int len;
                while ((len = inputStream.read(buffer)) != -1) {
                    fileOutputStream.write(buffer);
                }

                inputStream.close();
                fileOutputStream.close();

                ObjectMetadata metadata = result.getMetadata();
                Log.d("OSSFileHelper", "ContentType:" + metadata.getContentType());

                if (!subscriber.isUnsubscribed()) {
                    Log.d("OSSFileHelper", "success:" + outFile);
                    subscriber.onNext(outFile);
                }
            } catch (Throwable t) {

                Exceptions.throwIfFatal(t);
                if (!subscriber.isUnsubscribed()) {
                    subscriber.onError(t);
                }
                return;
            } finally {
                try {
                    if (inputStream != null)
                        inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    if (fileOutputStream != null)
                        fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (!subscriber.isUnsubscribed()) {
                subscriber.onCompleted();
            }
        }
    }


}
